Skip to content

Harden session persistence, worker recovery, and daemon refreshes - #2028

Merged
sethkarten merged 22 commits into
mainfrom
refactor/semantic-edges-ledger-on-event-log
Sep 7, 2026
Merged

Harden session persistence, worker recovery, and daemon refreshes#2028
sethkarten merged 22 commits into
mainfrom
refactor/semantic-edges-ledger-on-event-log

Conversation

@snimu

@snimu snimu commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Use atomic writes and zombie-aware lock ownership for durable session and daemon state.
  • Keep snapshot errors local to each transfer. Retry recoverable workers and return typed recovery errors.
  • Coalesce child-usage accounting while preserving own-session totals after reload. Reuse incremental session scans and passive topology results without caching transient read failures.
  • Fix provider billing and request fields, dead-kernel recovery, malformed rendering inputs, and session I/O edge cases.

Validation

  • Node 22: 1,184 coding-agent tests, 51 AI tests, and one TUI test passed. Two existing compaction tests remain skipped.
  • Python: 57 runtime tests passed.
  • npm run check passed, including lint, TypeScript, installer checks, and browser smoke checks.

Refs RES-1260.


Note

High Risk
Touches authentication storage, session transcript repair, daemon protocol/worker recovery, and billing-related provider fields—failures could corrupt sessions, block attach, or mis-price usage.

Overview
This PR hardens durable state and daemon/session I/O while fixing a few provider and runtime edge cases.

Persistence and transcripts: Durable JSON/JSONL writes go through shared atomic rename (writeFileAtomicSync), including auth.json (exclusive create + atomic update), settings, cron jobs, telemetry, and session rewrites. Session files get crash repair at open (torn tails, zero-filled lines) when the owning process persists; export and daemon-client read paths use in-memory managers so they do not repair another process’s file. The shared event-log tail rule is unified (unterminated final line skipped on read, truncated on append). Semantic-edge ledgers now append via that substrate.

Daemon performance and correctness: Session-list metadata uses incremental resumable scans with a bounded usage cache and serialized per-path reads. Passive subagent topology is memoized until ledger/roster/session inputs change. Snapshot transfer ids bind to the materialized event cursor so a mismatched chunked transfer fails locally (clients resync). Failed workers with a live process can be retried for recovery; known-but-not-ready sessions return session_recovering instead of “unknown session”. Zombie-aware liveness is shared for daemon ps, leases, supervisor ownership, and update-restart. Idle status sweeps coalesce journal writes and backoff repeated failed summary generations.

Session runtime: RLM child usage flushes one attribution batch per settle boundary (not per child message); live “own usage” subtracts not-yet-indexed child spend. Auto-retry ends cleanly when a scheduled continue() never starts. Compaction keeps only the final turn when the token budget is crossed inside trailing tool results. Bash/output spill paths degrade instead of crashing when temp files fail.

Providers (packages/ai): OpenAI Responses omit service_tier for GitHub Copilot (field rejected) but still send explicit tiers elsewhere. Anthropic streaming reprices cache writes from message_delta cache_creation breakdown when usage shifts after message_start.

Smaller fixes: Dead IPython kernels drop the memo and restart on next use; piped CLI input joins file/instruction with a blank line; zai default glm-5.3; WebP EXIF scan termination; tail truncation and frontmatter BOM handling; daemon protocol revision 27.

Reviewed by Cursor Bugbot for commit 1c2c9f2. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Harden session persistence with atomic writes, crash repair, and worker recovery

  • Adds shared writeFileAtomicSync in atomic-file.ts and migrates session, auth, event-log, daemon, and harness persistence from bespoke temp-file/rename sequences to the shared utility, preserving symlinks and file modes
  • Adds session-file crash damage detection (tailLooksDamaged) and repair (repairJsonlDamage) in session-manager.ts that removes zero-filled or torn JSONL tails at open before subsequent appends
  • Replaces size-and-mtime session cache with resumable incremental scanning in readSessionInfo/scanSessionInfo, so repeated reads resume from the last complete line and concurrent reads for the same path execute in order
  • Reworks daemon worker recovery in daemon-supervisor.ts so failed workers with a current process identity can retry recovery, concurrent commands share one in-flight recovery, and snapshot transfer failures trigger client resync only when the failed transfer matches the published snapshot
  • Adds zombie-aware process liveness checks in child-process.ts and daemon-supervisor-ownership.ts with a 5-second probe cache, so zombie owners are treated as reclaimable without probing on every fence poll
  • Risk: readSessionInfo now serializes same-path reads through a per-path queue and requires a session header as the first parsed entry; files whose first entry is not a header will return null. EventLog.replaySync now skips any unterminated final line including one that parses as valid JSON, so previously-tolerated partial tails are dropped on replay

Changes since #2028 opened

  • Introduced read-only session management mode that prevents repairs and writes when loading sessions as a daemon client, during HTML export, or when rendering active daemon summaries [1c2c9f2]
  • Implemented deferred child usage attribution with unindexed usage tracking to ensure own usage reporting excludes child usage that has been accounted live but not yet durably indexed [1c2c9f2]
  • Replaced single-write file initialization with loop-based complete writes that fail fast on short writes for credentials storage and preserved file mode across atomic replacements in harness state saves [1c2c9f2]
  • Removed early-break special case for current file key in session scan cache trimming to enforce retained usage entry cap strictly [1c2c9f2]
  • Added test coverage for read-only session behaviors, unindexed child usage handling, file write safety under partial writes, session scan cache limit enforcement, and permission preservation across atomic saves [1c2c9f2]

Macroscope summarized 5ccfad6.

…log substrate

The recorder's private append/replay/repair IO is deleted; EventLog owns it, the same move #1987 made for the RLM spawn ledger. One durability rule is unified in the substrate rather than dropped: an unterminated final line is an uncommitted append, skipped on read and truncated before the next append — never newline-completed and never surfaced to a consumer whose next append destroys it.
Comment thread packages/coding-agent/src/core/semantic-edges.ts Outdated
…atomic

readSemanticEdgeLedger probed with statSync before reading through EventLog, which swallows ENOENT; a ledger deleted between the two returned [] instead of throwing. The missing-file decision now lives at the single open (replaySync missingFileThrows), so no check-then-read window exists.
The unterminated-tail contract was restated four times (module doc, replaySync doc, two test comments). It now lives once in the module doc; the method doc keeps only its own parse/missing-file semantics and the test comments reference the contract.
…tail repair

writeSync may write short (ENOSPC after a prefix); appendSync now loops until the payload is fully on disk so write-before-action callers never act on a torn record reported as success. A tail-repair failure (e.g. append-only ACL permitting O_APPEND but not r+) now propagates instead of being swallowed: writing through an unrepaired torn tail would weld it to the new record as permanent interior corruption. ENOENT and the concurrent-writer instability path keep their existing semantics.
Comment thread packages/coding-agent/src/core/event-log.ts Outdated
Comment thread packages/coding-agent/src/core/event-log.ts
…ng them

The rlm spawn ledger is multi-writer by documented design (supervisor plus each worker over one file), so completing a short O_APPEND write with a second write could interleave with a rival append and weld two records. A short write now truncates its own torn prefix back off (only while this writer still owns the tail) and fails the append; a torn tail is read-tolerated, a weld is permanent corruption. The append fd opens a+ so the ownership check can read the tail.
Comment thread packages/coding-agent/src/core/event-log.ts Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread packages/coding-agent/src/core/event-log.ts Outdated
snimu and others added 15 commits September 4, 2026 17:37
…claiming it

The tail-match reclaim could truncate a rival's committed record whose final bytes coincide with our torn prefix - committed-data loss, strictly worse than the torn tail it prevented. A short write now just fails the append: the torn tail is the one tolerated shape, skipped on read and truncated by any writer's next repair (verified for both topologies: a resumed single-writer recorder repairs on its first append; every rlm-ledger writer repairs before each append).
…sage_delta, repoint the zai default

Incorporates #2032 at f82c7fa.
…; mismatches settle the transfer, not the worker channel

Incorporates #2044 at 5af3bbe.
…pdates

Keep durable child-usage aggregates separate from pending sibling usage. Retry optional topology metadata after transient reads. Completes #2050 and #2051 integration.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 5ccfad6. Configure here.

Comment thread packages/coding-agent/src/core/agent-session.ts Outdated
Comment thread packages/coding-agent/src/core/event-log.ts
Comment thread packages/coding-agent/src/utils/child-process.ts
Comment thread packages/coding-agent/src/core/session-manager.ts Outdated
Comment thread prime-agent-runtime/src/rlm/harness.py
Comment thread packages/coding-agent/src/core/refinement/refinement.ts
Comment thread packages/coding-agent/src/core/session-manager.ts
Comment thread packages/coding-agent/src/core/agent-session.ts
Comment thread packages/coding-agent/src/core/auth-storage.ts Outdated
Comment thread prime-agent-runtime/src/rlm/harness.py
Comment thread packages/coding-agent/src/core/agent-session.ts
@sethkarten
sethkarten self-requested a review September 7, 2026 01:55
Comment thread packages/coding-agent/src/core/session-manager.ts
Comment thread packages/coding-agent/src/core/auth-storage.ts Outdated
Comment thread prime-agent-runtime/src/rlm/harness.py
Comment thread packages/coding-agent/src/core/agent-session.ts
@sethkarten
sethkarten merged commit 844e855 into main Sep 7, 2026
23 checks passed
@sethkarten
sethkarten deleted the refactor/semantic-edges-ledger-on-event-log branch September 7, 2026 01:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants